/**
 * 实体类
 */
import tnx from '@/tnx.js';
import StandardClass from './StandardClass.js';
import EntityProperty from './EntityProperty.js';
import TypeKind from '../enums/TypeKind.js';
import InheritanceStrategy from '../enums/InheritanceStrategy.js';
import DiscriminatorColumn from './DiscriminatorColumn.js';
import PhysicalIndex from './PhysicalIndex.js';

export default class EntityClass extends StandardClass {

    /**
     * 继承策略
     * @type {InheritanceStrategy}
     */
    inheritanceStrategy = null;
    tableName = '';
    /**
     * 鉴别列配置，作为父类且采用共用单表的继承映射策略时有效
     * @type {DiscriminatorColumn}
     */
    discriminatorColumn = null;
    discriminatorValue = '';
    /**
     * @type {[EntityProperty]}
     */
    properties = [];
    /**
     * 参与关键字模糊搜索的映射id集合
     * @type {[String]}
     */
    keywordMappingIds = [];
    /**
     * @type {[PhysicalIndex]}
     */
    indexes = [];

    constructor(type, packageName, simpleName) {
        type = TypeKind.validate(type, 'entity');
        super(type, packageName, simpleName);
    }

    static of(object) {
        if (typeof object !== 'object') {
            return undefined;
        }
        let clazz = new EntityClass(object.kind, object.packageName, object.simpleName);
        clazz.update(object);
        clazz.ensurePropertyId();
        return clazz;
    }

    update(object) {
        super.update(object);
        if (typeof object === 'object') {
            if (object.inheritanceStrategy) {
                this.inheritanceStrategy = InheritanceStrategy.validate(object.inheritanceStrategy);
            }
            if (object.discriminatorColumn) {
                this.discriminatorColumn = DiscriminatorColumn.of(object.discriminatorColumn);
            }
            if (Array.isArray(object.properties)) {
                this.properties = object.properties.map(property => EntityProperty.of(property));
            }
            if (Array.isArray(object.keywordMappingIds)) {
                this.keywordMappingIds = [...object.keywordMappingIds];
            }
            if (Array.isArray(object.indexes)) {
                this.indexes = object.indexes.map(index => PhysicalIndex.of(index));
            }
        }
        return this;
    }

    clone() {
        return EntityClass.of(this);
    }

    ensurePropertyId() {
        if (this.properties) {
            for (let property of this.properties) {
                if (property.primary) {
                    if (!property.id) {
                        // 主键属性的id相对固定，通过类id和属性名生成
                        property.id = tnx.util.string.md5(this.id + '.' + property.name);
                    }
                } else {
                    property.ensureId();
                }
            }
        }
    }

    existsPrimaryProperty() {
        return !!this.getPrimaryProperty();
    }

    getPrimaryLogicTypeIds(toPrimitive = false) {
        let typeIds = [];
        for (let property of this.properties) {
            if (property.primary) {
                // 主键类型只能是java.lang.String、java.lang.Long、java.lang.Integer，无需做转换
                let typeId = property.typeId;
                if (toPrimitive) {
                    if (typeId.startsWith('java.lang.')) {
                        let simpleName = typeId.substring(10);
                        if (simpleName !== 'String') {
                            if (simpleName === 'Integer') {
                                typeId = 'int';
                            } else {
                                typeId = simpleName.firstToLowerCase();
                            }
                        }
                    }
                }
                typeIds.push(typeId);
            }
        }
        return typeIds;
    }

    getPrimaryLogicNames() {
        let logicNames = [];
        for (let property of this.properties) {
            if (property.primary) {
                logicNames.push(property.name);
            }
        }
        return logicNames;
    }

    findDuplicatedPhysicalFieldName() {
        if (this.properties?.length) {
            let seen = new Map();
            for (let property of this.properties) {
                if (property.mapping) {
                    if (seen.has(property.mapping.name)) {
                        return property.mapping.name;
                    }
                    seen.set(property.mapping.name, true);
                }
                if (property.embedded?.length) {
                    for (let embeddedMapping of property.embedded) {
                        if (seen.has(embeddedMapping.name)) {
                            return embeddedMapping.name;
                        }
                        seen.set(embeddedMapping.name, true);
                    }
                }
            }
        }
        return null;
    }

    getMappingNames(mappingIds) {
        let mappingNames = [];
        for (let property of this.properties) {
            let mapping = property.mapping;
            if (mapping && mappingIds.includes(mapping.id)) {
                mappingNames.push(mapping.name);
            }
            if (property.embedded) {
                for (let embeddedMapping of property.embedded) {
                    if (mappingIds.includes(embeddedMapping.id)) {
                        mappingNames.push(embeddedMapping.name);
                    }
                }
            }
        }
        return mappingNames;
    }

    isKeywordInvolved() {
        for (let property of this.properties) {
            if (property.embedded?.length) {
                for (let embeddedMapping of property.embedded) {
                    if (this.keywordMappingIds.includes(embeddedMapping.id)) {
                        return true;
                    }
                }
            } else if (this.keywordMappingIds.includes(property.mapping?.id)) {
                return true;
            }
        }
        return false;
    }

}
